package com.test;

import com.sun.deploy.util.ArrayUtil;
import com.sun.org.apache.xpath.internal.functions.FuncFalse;
import sun.util.resources.cldr.ta.CurrencyNames_ta;

import java.io.*;
import java.net.SocketTimeoutException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

public class Main {
    private final static String sourceExt = "smp";
    private final static String saveExt = "mp3";

    public static void main(String[] args) throws IOException {
        //加密字符串
        byte[] key;
        //文件所在根文件夹
        File root;

        List<String> filePath = new LinkedList<String>();

        Scanner scanner = new Scanner(System.in);
        //获取文件根目录
        System.out.println("请输入smp文件根目录，支持深度检索");
        do {
            String rootPath = scanner.nextLine();
            root = new File(rootPath);
            if (!root.exists() || !root.isDirectory()) {
                System.out.println("根目录错误，请重新输入");
                continue;
            }
            break;
        } while (true);

        //检索文件
        System.out.println("开始检索文件");
        List<File> files = deptScanFiles(root);
        System.out.println("检索到" + files.size() + "个文件");
        for (int i = 0; i < files.size(); i++) {
            System.out.print("[" + (i + 1) + "] ");
            System.out.println(files.get(i));
        }
        do {
            System.out.println("直接回车进行下一步，或者输入文件序号查看文件hex");
            String temp = "0" + scanner.nextLine().trim();
            Integer tempI = Integer.valueOf(temp);
            if (tempI > files.size() || tempI < 0) {
                System.out.println("输入异常，请重新输入");
                continue;
            }
            if (tempI.equals(0)) {
                break;
            }
            String savePath = saveFile2Hex(files.get(tempI - 1));
            System.out.println("文件已保存到" + savePath);
        } while (true);

        //设置加密信息
        System.out.println("请输入16进制加密信息，如：1A 2B 3C");
        do {
            String tempKey = scanner.nextLine().toUpperCase(Locale.ROOT);

            if (tempKey == null || tempKey == "") {
                System.out.println("请输入16进制加密信息");
                continue;
            }
            //判断16进制
            String regex = "^[A-Fa-f0-9 ]+$";
            if (tempKey.matches(regex)) {
                String[] ks = tempKey.split(" ");
                key = new byte[ks.length];
                for (int i = 0; i < ks.length; i++) {
                    key[i] = (byte) Integer.parseInt(ks[i], 16);
                }
                break;
            } else {
                System.out.println("不是16进制,请重新输入");
                continue;
            }
        } while (true);


        //读取smp文件字符串，对key再进行异或后保存成MP3
        for (File file : files) {
            byte[] bytes = readFile2Byte(file);
            decrypt(bytes, key);
            saveBytes2File(bytes, new File(file.getParent(), file.getName().substring(0, file.getName().indexOf(".") + 1) + saveExt).getAbsolutePath());
            System.out.println(String.format("文件%s已处理", file.getAbsolutePath()));
        }
        System.out.println("处理完成");
    }

    /**
     * 深度扫描smp文件
     *
     * @param root
     * @return
     */
    public static List<File> deptScanFiles(File root) {
        if (!root.exists()) {
            return null;
        }
        if (root.isFile()) {
            String fileExtension = getFileExtension(root);
            if (sourceExt.equals(fileExtension)) {
                return new LinkedList<File>() {{
                    add(root);
                }};
            } else {
                return new LinkedList<File>();
            }
        } else if (root.isDirectory()) {
            LinkedList<File> files = new LinkedList<>();
            for (File file : root.listFiles()) {
                files.addAll(deptScanFiles(file));
            }
            return files;
        } else {
            return new LinkedList<File>();
        }
    }

    /**
     * 获取文件后缀
     *
     * @param file
     * @return
     */
    public static String getFileExtension(File file) {
        String fileName = file.getName().toLowerCase(Locale.ROOT);
        if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) {
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        } else {
            return "";
        }
    }

    /**
     * 按照
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static byte[] readFile2Byte(File file) throws IOException {
        FileInputStream fin = new FileInputStream(file);
        int len = fin.available();

        byte[] temp = new byte[len];
        fin.read(temp);

        return temp;
    }

    /**
     * 解密
     *
     * @param source
     * @param keys
     */
    public static void decrypt(byte[] source, byte[] keys) {
        int idx = 0;
        for (int i = 0; i < source.length; i++) {
            source[i] ^= keys[i % keys.length];
        }
    }

    /**
     * 按16进制保存文件
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static String saveFile2Hex(File file) throws IOException {
        String savePath = file.getAbsolutePath() + ".hex.txt";
//        System.out.println(System.currentTimeMillis());
        byte[] bytes = readFile2Byte(file);
        StringWriter sw = new StringWriter();
        int currentLen = 0;
        for (int i = 0; i < bytes.length; i++) {
            currentLen += 1;
            if (bytes[i] >= 0x0) {
                sw.write(String.format("%02x", bytes[i]).toUpperCase(Locale.ROOT));
            } else {
                sw.write(Integer.toHexString(bytes[i]).substring(6).toUpperCase(Locale.ROOT));
            }

            sw.write(" ");
            if (currentLen >= 16) {
                currentLen = 0;
                sw.write("\r\n");
            }
        }
//        System.out.println(System.currentTimeMillis());
        Files.write(Paths.get(savePath), bytes);
//        System.out.println(System.currentTimeMillis());
        return savePath;
    }

    public static boolean saveBytes2File(byte[] bytes, String filePath) throws IOException {

//        System.out.println(System.currentTimeMillis());
        Files.write(Paths.get(filePath), bytes);
//        System.out.println(System.currentTimeMillis());
        return true;
    }
}
